import { createNamespacedHelpers } from 'vuex'
import { unique } from '../string'
import Sidemenu from './sidemenu'
// const APPS = {}
// const ACCESS = {}

function resolvePathParams (path, params) {
  if (!path || !params) {
    return path
  }
  /* eslint-disable */
  return path.replace(/\:(\w+)/g, (m1, m2) => {
    if (params[m2]) {
      return params[m2]
    } else {
      return m1
    }
  })
  /* eslint-enable */
}

/**
 * 递归路由
*/
export function loopRoutes (routes, loopFn) {
  if (!loopFn) {
    return
  }
  const loop = (routes) => {
    if (!routes || !routes.length) {
      return
    }
    return routes.some((route, index) => {
      if (loopFn) {
        const isBreak = loopFn({
          index,
          route,
          update: (newRoute) => {
            if (!newRoute) {
              return
            }
            routes.splice(index, 1, newRoute)
          },
          remove: () => {
            routes.splice(index, 1)
          }
        })
        if (isBreak) {
          return true
        }
      }
      return loop(route.children)
    })
  }
  loop(routes)
  // return result
}

export function createPlugin (options) {
  const plugin = Object.assign({
    id: null,
    name: null,
    title: null,
    icon: null,
    color: null,
    onCreated: null,
    onLaunch: null,
    render: null
  }, options)
  if (!plugin.id) {
    throw new Error('[App] 缺少插件模块id')
  }
  if (!plugin.name) {
    throw new Error('[App] 缺少插件模块name')
  }
  return plugin
}

export function createLicences (options) {
  const licences = Object.assign({
    id: null,
    name: null,
    title: null,
    icon: null,
    color: null,
    onCreated: null,
    onLaunch: null
  }, options)
  if (!licences.id) {
    throw new Error('[App] 缺少授权模块id')
  }
  if (!licences.name) {
    throw new Error('[App] 缺少授权模块name')
  }
  licences.getRoutes = () => {
    if (!licences.router) {
      return []
    }
    return licences.router.routes || []
  }
  return licences
}

export function createModule (options) {
  const mod = Object.assign({
    id: null,
    name: null,
    title: null,
    icon: null,
    color: null,
    router: null,
    params: null, // 注入的参数，一般创建模版模块时会动态注入一些参数
    setting: null,
    slots: [],
    apis: [],
    /**
     * {
     *    name1: {
     *      state: {},
     *      getters: {},
     *      actions: {}
     *    },
     *    name2: {}
     * }
    */
    store: null,
    /**
     * [{
     *    id: 'xxx',
     *    name: 'CrmContract',
     *    title: '合同审批表单',
     *    component: null
     * }]
    */
    approves: [],
    components: [],
    onCreated: null
  }, options)
  mod._module = true
  mod._app = null
  if (!mod.id) {
    throw new Error('[App] 缺少模块id')
  }
  if (!mod.name) {
    throw new Error('[App] 缺少模块name')
  }
  if (mod && mod.setting && mod.setting.modules) {
    mod.setting.modules.forEach(item => {
      if (!item._module) {
        throw new Error('setting中必须是模块')
      }
    })
  }
  const slotComponentMap = {}
  /**
   * 模块插槽
   * @param { String } slotName - 插槽名称
   * @param { Object } renderCompoent - 渲染的组件
   * @param { Function } sandboxFn - 上下文回调函数
   * @example
   * addSlotComponent('testSlot', {
   *    title: '跟进组件',
   *    render (data) {
   *       return {
   *          name: 'VueComponent',
   *          data () {
   *
   *          },
   *          created () {
   *
   *          }
   *       }
   *    }
   * })
  */
  mod.addSlotComponent = (slotName, renderCompoent, defaultProps, sandboxFn) => {
    if (!slotName) {
      return
    }
    if (!mod.slots || !mod.slots.length) {
      throw new Error(`[App] 模块${mod.title}不支持${slotName}插槽`)
    }
    if (typeof defaultProps === 'function') {
      sandboxFn = defaultProps
      defaultProps = null
    }
    const slotDefined = mod.slots.find(v => v.name === slotName)
    if (!slotDefined) {
      throw new Error(`[App] 模块${mod.title}不支持${slotName}插槽`)
    }
    if (!slotComponentMap[slotName]) {
      slotComponentMap[slotName] = []
    }
    if (!renderCompoent) { // 有可能被权限过滤掉了，所以直接return
      return
    }
    if (!renderCompoent.id) {
      throw new Error('[App] 插槽组件id必须')
    }
    if (!renderCompoent.title) {
      throw new Error('[App] 插槽组件title必须')
    }
    if (!renderCompoent.intercept) {
      throw new Error('[App] 插槽组件intercept必须')
    }
    if (sandboxFn && typeof sandboxFn !== 'function') {
      throw new Error('[App] 上下文执行函数必须是function类型')
    }
    const nextRenderComponent = Object.assign({}, renderCompoent, {
      mixins: [].concat(renderCompoent.mixins || [], [{
        props: {
          $slotComponent: {
            type: Object
          }
        }
      }])
    })
    const nextComponent = {
      id: renderCompoent.id,
      title: renderCompoent.title,
      intercept: renderCompoent.intercept,
      apis: renderCompoent.apis || {},
      props: (slotDefined.props || []).reduce((rs, v) => {
        rs[v.name] = {
          type: v.type,
          required: v.required,
          default: v.default,
          validator: v.validator,
          description: v.description
        }
        return rs
      }, {}),
      render (h) {
        return h(nextRenderComponent, {
          props: Object.assign({}, this.$props, defaultProps, {
            $slotComponent: Object.assign({
              id: renderCompoent.id,
              title: renderCompoent.title,
              name: slotName
            }, nextComponent.apis)
          }),
          on: this.$listeners,
          ref: 'comp'
        })
      },
      methods: {
        execMethod (methodName, ...args) {
          if (!this.$refs.comp) {
            return
          }
          if (!this.$refs.comp[methodName]) {
            return
          }
          return this.$refs.comp[methodName].apply(this.$refs.comp, args)
        }
      },
      sandboxFn
    }
    slotComponentMap[slotName].push(Object.assign({}, nextComponent))
  }
  /**
   * 获取所有插槽下的组件集合
   * @param { String } slotName - 插槽名称
   * @param { Object } contextObject - 上下文实例对象
  */
  mod.getSlotComponents = (slotName, contextObject) => {
    const slotDefined = mod.slots.find(v => v.name === slotName)
    if (!slotDefined) {
      throw new Error(`[App] 模块${mod.title}不支持${slotName}插槽`)
    }
    if (!contextObject) {
      throw new Error(`[App] ${slotName}上下文实例不能为空`)
    }
    const contextApis = (slotDefined.apis || []).reduce((rs, methodName) => {
      rs[methodName] = (...args) => {
        if (!contextObject[methodName]) {
          throw new Error(`[App] 上下文实例未实现${methodName}方法`)
        }
        if (typeof contextObject[methodName] !== 'function') {
          throw new Error(`[App] 上下文实例${methodName}不是function类型`)
        }
        contextObject[methodName].apply(contextObject, args)
      }
      return rs
    }, {})
    return (slotComponentMap[slotName] || []).reduce((rs, v) => {
      if (v.intercept) {
        if (v.intercept() !== false) {
          rs.push(v)
        }
      } else {
        rs.push(v)
      }
      return rs
    }, []).map(slotComponent => {
      if (slotComponent.sandboxFn) {
        slotComponent.sandboxFn(contextApis, slotComponent)
      }
      return slotComponent
    })
  }
  mod.getComponent = (componentName) => {
    if (!mod.components || !mod.components[componentName]) {
      return
    }
    if (mod.components[componentName].intercept && mod.components[componentName].intercept() === false) {
      return
    }
    return mod.components[componentName]
  }
  mod.getApi = (apiName) => {
    if (!mod.apis) {
      return
    }
    return mod.apis.find(v => v.name === apiName)
  }
  mod.getRoutes = () => {
    if (!mod.router) {
      return []
    }
    const loop = (routes) => {
      if (!routes || !routes.length) {
        return
      }
      return routes.map(route => {
        if (!route.meta) {
          route.meta = {}
        }
        route.meta.$id = route.id
        route.meta.$moduleId = mod.id
        if (route.component) {
          if (typeof route.component === 'object') {
            route.component = Object.assign({}, route.component, {
              $moduleId: mod.id
            })
          } else if (typeof route.component === 'function') {
            const origCompoent = route.component
            route.component = () => {
              return origCompoent().then(rs => {
                const component = rs.default ? rs.default : rs 
                return Object.assign({}, component, {
                  $moduleId: mod.id
                })
              })
            }
          }
        }
        if (route.children && route.children.length) {
          route.children = loop(route.children)
        }
        return route
      })
    }
    return loop(mod.router.routes) || []
  }
  mod._updateRoutes = (fn) => {
    if (!mod.router) {
      return
    }
    const loop = (routes) => {
      if (!routes || !routes.length) {
        return
      }
      routes.forEach(route => {
        fn({
          route: Object.assign({}, route),
          updateId (id) {
            route.id = id
            if (!route.meta) {
              route.meta = {}
            }
            route.meta.$id = id
          },
          updateOwnerModuleId (moduleId) {
            if (!route.meta) {
              route.meta = {}
            }
            route.meta.$moduleId = moduleId
          }
        })
        loop(route.children)
      })
    }
    loop(mod.router.routes)
  }
  mod.getStoreOptions = () => {
    return options.store || {}
  }
  mod._setApp = (app) => {
    mod._app = app
  }
  let cacheStore = null
  Object.defineProperty(mod, 'store', {
    get () {
      if (cacheStore) {
        return cacheStore
      }
      const paths = [mod._app.name, mod.name].join('/')
      return {
        ...createNamespacedHelpers(paths),
        getGetter (name) {
          const ns = `${mod.name}/${name}`
          return mod._app.getters[ns]
        },
        dispatch (name, playload) {
          const ns = `${mod.name}/${name}`
          return mod._app.dispatch(ns, playload)
        }
      }
    }
  })
  return mod
}

export function createApp (options) {
  options = Object.assign({
    id: null,
    name: null,
    title: null,
    plugins: [],
    licences: [],
    access: {},
    /**
     * {
     *    icon: null,
     *    title: '',
     *    modules: []
     * }
    */
    setting: {},
    modules: [],
    router: null,
    path: null,
    slots: [],
    templates: [], // 功能模版集合
    store: null,
    apis: [],
    components: {},
    /**
     * [{
     *      id: String, // 消息发送者id
     *      title: String, // 消息发送者中文名
     *      icon: String,  // 消息发送者图标
     *      name: String,  // 消息发送者英文名
     *      formatter: Object // 消息格式化，可以覆盖同名默认的
    *    }]
    */
    message: null,
    onCreated: null // 应用间依赖都准备完毕，准备创建到项目时触发事件
  }, options)
  if (!options || !options.name) {
    throw new Error('缺少应用名称')
  }
  let _platform = null
  const app = {
    id: options.id,
    name: options.name,
    icon: options.icon,
    title: options.title,
    path: options.path,
    router: options.router,
    slots: options.slots,
    apis: options.apis,
    plugins: options.plugins,
    access: options.access,
    modules: options.modules,
    message: options.message,
    templates: options.templates,
    licences: options.licences,
    approve: options.approve,
    setting: options.setting,
    store: options.store,
    components: options.components
  }
  app.modules = [].concat(app.modules || []) // 简单去除引用关系
  if (app && app.setting && app.setting.modules) {
    app.setting.modules.forEach(item => {
      if (!item._module) {
        throw new Error('setting中必须是模块')
      }
    })
  }
  if (app.modules && app.modules.length) {
    app.modules.forEach(mod => {
      mod._installed = true // 记录一下模块已经安装，后面replaceModule的时候要用此属性判断，不能替换为已安装的模块，防止功能乱套
      mod._setApp(app)
    })
  }
  const dependentApis = []
  const dependentComponents = []
  const dependentTemplates = []
  let cacheRoutes = null
  let cacheSettingRoutes = null
  // let isCreated = false
  // let isLaunch = false
  function fireHook (hookName) {
    return (context) => {
      const loopModule = (mods) => {
        if (!mods || !mods.length) {
          return
        }
        mods.forEach(mod => {
          if (mod && mod[hookName]) {
            mod[hookName](Object.assign({}, context, {
              app,
              module: mod
            }))
          }
          loopModule(mod.modules)
        })
      }
      const loopPlugin = (plugins) => {
        if (!plugins || !plugins.length) {
          return
        }
        plugins.forEach(plugin => {
          if (plugin && plugin[hookName]) {
            plugin[hookName](Object.assign({}, context, {
              app,
              plugin
            }))
          }
        })
      }
      const loopLicences = (licences) => {
        if (!licences || !licences.length) {
          return
        }
        licences.forEach(licencesItem => {
          if (licencesItem && licencesItem[hookName]) {
            licencesItem[hookName](Object.assign({}, context, {
              app,
              licencesItem
            }))
          }
        })
      }
      loopModule(app.modules)
      loopModule(app.setting ? app.setting.modules : [])
      loopPlugin(app.plugins)
      loopLicences(app.licences)
      if (options[hookName]) {
        options[hookName]()
      }
    }
  }
  app.fireCreated = fireHook('onCreated') // 初始数据准备完毕，可以在这个阶段安装模版，改一些已有数据结构
  app.fireLaunch = fireHook('onLaunch') // 路由数据准备完毕，可以在这里改路由，因为上面还在改路由，可能路由还没准备好，这里改路由安全一些
  app.fireReady = fireHook('onReady') // 数据全都准备完毕
  let getMenuMethod = null
  let ready = false
  app._setReady = () => {
    ready = true
  }
  app.setGetMenuMethod = fn => {
    getMenuMethod = fn
  }
  app.getMenuData = () => {
    if (!getMenuMethod) {
      throw new Error('未初始化getMenuMethod方法')
    }
    return getMenuMethod({
      app
    })
  }
  app.getStoreOptions = () => {
    const storeOptions = options.store || {}
    if (!storeOptions.modules) {
      storeOptions.modules = {}
    }
    if (app.modules && app.modules.length) {
      app.modules.reduce((rs, mod) => {
        const modStoreOptions = mod.getStoreOptions()
        if (modStoreOptions) {
          // Object.keys(v.store).forEech(key => {
          //   if (rs[key]) {
          //     throw new Error(`${key}的store已经被注册`)
          //   }
          //   rs[key] = v.store
          // })
          rs[mod.name] = modStoreOptions
        }
        return rs
      }, storeOptions.modules)
    }
    return storeOptions
  }
  app.addRoutes = (route, childRoutes) => {
    if (!route) {
      return
    }
    if (!childRoutes || !childRoutes.length) {
      return
    }
    if (!route.children) {
      route.children = []
    }
    childRoutes.forEach(v => {
      route.children.push(v)
    })
  }
  app.getPlugins = () => {
    return app.plugins || []
  }
  app.getLicences = () => {
    return app.licences || []
  }
  app.getRoutes = () => {
    if (cacheRoutes) {
      return cacheRoutes
    }
    const appRoutes = (app.router || {}).routes || []
    let moduleRoute = getModuleRootRoute(appRoutes)
    const settingRoute = {
      id: app.id + '_setting',
      title: app.setting.title || '设置',
      icon: app.setting.icon,
      path: '/' + app.name + '/' + 'setting',
      component: Sidemenu,
      _settingRoute: true,
      children: getSettingRoutes()
    }
    if (app.modules && app.modules.length) {
      let childrenRoutes = []
      app.modules.forEach((mod) => {
        childrenRoutes = childrenRoutes.concat(mod.getRoutes())
      }, [])
      // moduleRoute.children = childrenRoutes
      childrenRoutes.forEach(route => {
        if (moduleRoute) {
          moduleRoute.children.push(route)
        } else {
          appRoutes.push(route)
        }
      })
    }
    if (settingRoute.children && settingRoute.children.length) {
      appRoutes.push(settingRoute)
    }
    cacheRoutes = appRoutes
    return cacheRoutes
  }
  app.getRoute = (routeId) => {
    let result
    if (!routeId) {
      return result
    }
    const loop = (routes) => {
      if (!routes || !routes.length) {
        return
      }
      return routes.some(route => {
        if (route.id === routeId) {
          result = route
          return true
        }
        return loop(route.children)
      })
    }
    loop(app.getRoutes())
    return result
  }
  app.getAccess = () => {
    const prefix = `access_${app.name}`
    return (app.modules || []).reduce((rs, mod) => {
      if (mod.access) {
        Object.keys(mod.access).forEach(k => {
          if (k.indexOf(prefix) !== 0) {
            throw new Error(`[App] 权限点必须以${prefix}开头`)
          }
          rs[k] = mod.access[k]
        })
      }
      return rs
    }, {})
  }
  function getModuleRootRoute (routes) {
    let result = null
    if (!routes || !routes.length) {
      return result
    }
    const loop = (routes) => {
      if (!routes || !routes.length) {
        return
      }
      return routes.some(route => {
        if (route.moduleRoot) {
          result = route
          return true
        }
        if (loop(route.children)) {
          return true
        }
      })
    }
    loop(routes)
    return result
  }
  function getSettingRoutes () {
    if (cacheSettingRoutes) {
      return cacheSettingRoutes
    }
    const settingModules = (app.modules || []).reduce((rs, mod) => {
      if (mod.setting && mod.setting.modules && mod.setting.modules.length) {
        rs = rs.concat(mod.setting.modules)
      }
      return rs
    }, [].concat(app.setting.modules || []))
    cacheSettingRoutes = []
    settingModules.forEach(mod => {
      if (mod && mod.router && mod.router.routes) {
        cacheSettingRoutes = [].concat(cacheSettingRoutes, mod.router.routes)
      }
    })
    return cacheSettingRoutes
  }
  app.getApi = (apiName) => {
    let result
    if (app.apis) {
      result = app.apis.find(v => v.name === apiName)
    }
    if (!result && app.modules && app.modules.length) {
      app.modules.some(mod => {
        result = mod.getApi(apiName)
        if (result) {
          return true
        }
      })
    }
    return result
  }
  app.getComponent = (componentName) => {
    let result
    if (app.components && app.components[componentName]) {
      if (app.components[componentName].intercept && app.components[componentName].intercept() !== false) {
        result = app.components[componentName]
      }
    }
    if (!result && app.modules && app.modules.length) {
      app.modules.some(mod => {
        if (!mod.components || !mod.components[componentName]) {
          return
        }
        if (mod.components[componentName].intercept && mod.components[componentName].intercept() === false) {
          return
        }
        result = mod.components[componentName]
        return true
      })
    }
    return result
  }

  app.addDependentApi = (apiName, api) => {
    if (!apiName) {
      throw new Error('[App]必须命名依赖API')
    }
    if (!api) {
      throw new Error('[App]依赖API不能为空')
    }
    if (dependentApis.some(v => v.name === apiName)) {
      throw new Error(`[App]已经存在同名的依赖API${apiName}`)
    }
    dependentApis.push({
      name: apiName,
      api
    })
  }
  app.getDependentApi = (apiName) => {
    const result = dependentApis.find(v => v.name === apiName)
    if (!result) {
      return
    }
    return result.api
  }

  app.addDependentComponent = (componentName, component) => {
    if (!componentName) {
      throw new Error('[App]必须重新命名依赖组件')
    }
    if (!component) {
      throw new Error('[App]依赖组件不能为空')
    }
    if (dependentComponents.some(v => v.name === componentName)) {
      throw new Error(`[App]已经存在同名的依赖组件${componentName}`)
    }
    dependentComponents.push({
      name: componentName,
      component
    })
  }
  app.getDependentComponent = (componentName) => {
    const result = dependentComponents.find(v => v.name === componentName)
    if (!result) {
      return
    }
    return result.component
  }

  app.addDependentTemplate = (templateName, template) => {
    if (!templateName) {
      throw new Error('[App]必须重新命名依赖模版')
    }
    if (!template) {
      throw new Error('[App]依赖模版不能为空')
    }
    if (dependentTemplates.some(v => v.name === templateName)) {
      throw new Error(`[App]已经存在同名的依赖模版${templateName}`)
    }
    dependentTemplates.push({
      name: templateName,
      template
    })
  }
  app.getDependentTemplate = (templateName) => {
    const result = dependentTemplates.find(v => v.name === templateName)
    if (!result) {
      return
    }
    return result.template
  }
  app.getTemplate = (templateName) => {
    let result
    if (app.templates && app.templates.length) {
      const template = app.templates.find(v => v.name === templateName)
      if (!template) {
        return result
      }
      if (template.intercept) {
        if (template.intercept() !== false) {
          result = template
        }
      } else {
        result = template
      }
    }
    return result
  }
  app.installTemplate = (template, id, options) => {
    if (!template) {
      throw new Error('模版不能为空')
    }
    if (!id) {
      throw new Error('实例化id不能为空')
    }
    if (ready) {
      throw new Error('应用已经准备完毕，不允许在安装模版')
    }
    let hasTemplate = false
    hasTemplate = app.templates.find(v => v === template)
    if (!hasTemplate) {
      hasTemplate = (dependentTemplates || []).find(v => v.template === template)
    }
    if (!hasTemplate) {
      throw new Error('应用下没有此模版')
    }
    const moduleInstace = template.create(options)
    moduleInstace._updateRoutes(({ route, updateId, updateOwnerModuleId }) => {
      updateId(id + '_' + route.id)
      updateOwnerModuleId(moduleInstace.id)
    })
    return moduleInstace
  }
  app.addSlotComponent = (moduleName, slotName, renderCompoent, defaultProps, sandboxFn) => {
    const module = app.modules.find(v => v.name === moduleName)
    if (!module) {
      throw new Error(`[App] 应用${app.name} 没有注册 ${moduleName}模块`)
    }
    module.addSlotComponent(slotName, renderCompoent, defaultProps, sandboxFn)
  }
  app.getApproves = () => {
    return (app.modules || []).reduce((rs, v) => {
      if (v.approves && v.approves.length) {
        rs = rs.concat(v.approves)
      }
      return rs
    }, [])
  }
  app.getModule = (moduleId) => {
    return (app.modules || []).find(v => v.id === moduleId)
  }
  app.replaceModule = (moduleId, newModule) => {
    if (!moduleId) {
      throw new Error('缺少模块id')
    }
    if (!newModule) {
      throw new Error('缺少替换的模块实例')
    }
    if (!newModule._module) {
      throw new Error('替换的模块无效')
    }
    if (newModule._installed) {
      throw new Error('不允许替换为一个已经安装的模块')
    }
    if (ready) {
      throw new Error('应用已经准备完毕，不允许再替换模块')
    }
    let origModuleIndex = -1
    app.modules.some((v, i) => {
      if (v.id === moduleId) {
        origModuleIndex = i
        return true
      }
    })
    if (origModuleIndex >= 0) {
      app.modules.splice(origModuleIndex, 1, newModule)
    } else {
      if (app.setting && app.setting.modules && app.setting.modules.length) {
        origModuleIndex = -1
        app.setting.modules.some((v, i) => {
          if (v.id === moduleId) {
            origModuleIndex = i
            return true
          }
        })
        if (origModuleIndex >= 0) {
          app.setting.modules.splice(origModuleIndex, 1, newModule)
        } else {
          throw new Error('未找到被替换的模块' + moduleId)
        }
      } else {
        throw new Error('未找到被替换的模块' + moduleId)
      }
    }
  }
  app._setPlatform = (platform) => {
    _platform = platform
  }
  let cacheStore = null
  Object.defineProperty(app, 'store', {
    get () {
      if (cacheStore) {
        return cacheStore
      }
      return {
        ...createNamespacedHelpers(app.name),
        getGetter (name) {
          const ns = `${app.name}/${name}`
          return _platform.store.getters[ns]
        },
        dispatch (name, playload) {
          const ns = `${app.name}/${name}`
          return _platform.store.dispatch(ns, playload)
        }
      }
    }
  })
  return app
}

// /**
//  * 获取注册的插件
// */
// export function getPlugins (name) {
//   let plugins = []
//   Object.keys(APPS).forEach(key => {
//     const module = APPS[key]
//     if (module.plugins) {
//       plugins = plugins.concat(module.plugins)
//     }
//   })
//   if (name) {
//     plugins = plugins.filter(v => v.name === name)
//   }
//   return plugins
// }

export function create (options) {
  options = Object.assign({
    root: null,
    basePath: '/',
    router: null,
    apps: [],
    plugins: [],
    modules: [],
    access: {},
    onCreated: null,
    onLaunch: null
  }, options)
  const rootApp = createApp({
    name: '__root__',
    id: '__root__',
    plugins: options.plugins,
    access: options.access,
    router: options.router,
    modules: options.modules
  })
  let appRoutes = []
  let appStores = {
    modules: {}
  }
  // 合并权限点
  // Object.keys(options.access).forEach(k => {
  //   if (!Array.isArray(options.access[k])) {
  //     throw new Error('[App] 权限点必须是数组类型')
  //   }
  //   ACCESS[k] = options.access[k]
  // })
  // const storeModules = {}
  const allApps = [].concat([rootApp], options.apps || [])
  /**
   * 将路由转换成菜单数据
  */
  function transformRoutesToMenu (routes, rootPath) {
    const result = []
    if (!routes || !routes.length) {
      return []
    }
    const loop = (data, parentPath, parentChildren) => {
      if (!data || !data.length) {
        return
      }
      data.forEach(item => {
        // 排除动态参数路径，动态参数路径代码自己处理
        // if (item.path && item.path.indexOf(':') >= 0) {
        //   return
        // }
        const newItem = {
          routeId: item.id,
          id: unique(),
          virtual: item.virtual,
          tabs: item.tabs,
          title: item.title,
          icon: item.icon,
          description: item.description
        }
        if (!parentPath) {
          newItem.fullPath = item.path
        } else {
          if (item.path.indexOf('/') === 0) {
            newItem.fullPath = item.path
          } else {
            if (item.path) {
              newItem.fullPath = parentPath + '/' + item.path
            } else {
              newItem.fullPath = parentPath
            }
          }
        }
        if (item.children) {
          const itemChildren = []
          loop(item.children, newItem.fullPath, itemChildren)
          if (itemChildren.length) {
            newItem.children = itemChildren
          } else {
            delete newItem.children
          }
        }
        parentChildren.push(newItem)
      })
    }
    loop(routes, rootPath, result)
    return result
  }

  function resolveMenuPath (menu, params, checkFn) {
    if (!menu || (!params && !checkFn)) {
      return menu
    }
    const loop = (menuItem) => {
      if (!menuItem) {
        return
      }
      if (checkFn && checkFn({
        menuItem
      }) === false) {
        return
      }
      const nextMenuItem = {...menuItem}
      if (nextMenuItem.fullPath) {
        nextMenuItem.fullPath = resolvePathParams(nextMenuItem.fullPath, params)
      }
      if (nextMenuItem.children) {
        nextMenuItem.children = nextMenuItem.children.reduce((rs, v) => {
          const subMenuItem = loop(v)
          if (subMenuItem) {
            rs.push(subMenuItem)
          }
          return rs
        }, [])
      }
      return nextMenuItem
    }
    if (Array.isArray(menu)) {
      return menu.reduce((rs, v) => {
        const nextMenuItem = loop(v)
        if (nextMenuItem) {
          rs.push(nextMenuItem)
        }
        return rs
      }, [])
    } else {
      return loop(menu)
    }
  }

  const generateMenuData = (routes) => {
    if (!routes || !routes.length) {
      return []
    }
    const nextRoutes = transformRoutesToMenu(routes, '')
    const result = []
    const loop = (data, children, virtualChildren) => {
      if (!data || !data.length) {
        return
      }
      data.forEach(item => {
        // if (item.access) {
        //   if (typeof item.access === 'function') {
        //     if (!item.access()) {
        //       return
        //     }
        //   } else if (!checkRights(item.access)) {
        //     return
        //   }
        // }
        // tabs和virtual时只是不再菜单中显示，但是父级菜单还是要显示的，后面判断itemChildren的时候还要考虑virtualChilden长度
        // if (item.virtual || item.tabs) {
        //   virtualChildren.push(item)
        //   return
        // }
        if (item.children && item.children.length) {
          const itemChildren = []
          const itemVirtualChildren = []
          loop(item.children, itemChildren, itemVirtualChildren)
          if (!itemChildren.length && !itemVirtualChildren.length) {
            return
          }
          if (itemChildren.length) {
            item.children = itemChildren
          } else {
            delete item.children
          }
        }
        children.push(item)
      })
    }
    loop(nextRoutes, result, [])
    return result
  }
  let cacheMenuData = null
  const platform = {
    generateMenuData () {
      if (!cacheMenuData) {
        cacheMenuData = generateMenuData(appRoutes)
      }
      return cacheMenuData
    },
    getMenuData (params) {
      return resolveMenuPath(cacheMenuData, params, ({
        menuItem
      }) => {
        if (menuItem.virtual) {
          return false
        }
      })
    },
    getMenuItemByRouteId (routeId, params) {
      if (!cacheMenuData) {
        return
      }
      let result
      const loop = (data) => {
        if (!data || !data.length) {
          return
        }
        return data.some(item => {
          if (item.routeId === routeId) {
            result = item
            return true
          }
          return loop(item.children)
        })
      }
      loop(cacheMenuData)
      return resolveMenuPath(result, params)
    },
    getClosestMenuData (menuId, params) {
      const result = []
      if (!cacheMenuData) {
        return result
      }
      const loop = (data) => {
        if (!data || !data.length) {
          return
        }
        return data.some(item => {
          if (item.id === menuId) {
            result.push(item)
            return true
          }
          if (loop(item.children)) {
            result.push(item)
            return true
          }
        })
      }
      loop(cacheMenuData)
      return resolveMenuPath(result, params, ({
        menuItem
      }) => {
        if (menuItem.virtual) {
          return false
        }
      })
    },
    getChildrenMenuData (menuId, params) {
      let result = null
      if (!cacheMenuData) {
        return []
      }
      const loop = (data) => {
        if (!data || !data.length) {
          return
        }
        return data.some(item => {
          if (item.id === menuId) {
            result = item
            return true
          }
          return loop(item.children)
        })
      }
      loop(cacheMenuData)
      if (!result) {
        return []
      }
      return resolveMenuPath(result.children, params, ({
        menuItem
      }) => {
        if (menuItem.virtual) {
          return false
        }
      }) || []
    },
    /**
     * 获取所有的应用消息定义发送者
    */
    getAllMessage () {
      return (allApps || []).reduce((rs, app) => {
        if (app.message && Array.isArray(app.message)) {
          rs = rs.concat(app.message)
        }
        return rs
      }, [])
    },
    /**
     * 获取所有应用的注册审批
    */
    getAllApprove () {
      return (allApps || []).reduce((rs, app) => {
        rs = rs.concat(app.getApproves())
        return rs
      }, [])
    },
    getAllLicences () {
      return (allApps || []).reduce((rs, app) => {
        rs = rs.concat(app.getLicences())
        return rs
      }, [])
    },
    /**
     * 获取插件集合
    */
    getPluginsByCategory (categoryName) {
      const plugins = (allApps || []).reduce((rs, app) => {
        const plugins = app.getPlugins()
        if (plugins && plugins.length) {
          rs = rs.concat(plugins.filter(v => v.category === categoryName))
        }
        return rs
      }, [])
      const sortPlugins = []
      const notSortPlugins = []
      plugins.forEach(v => {
        if (v.sortIndex === null || v.sortIndex === undefined) {
          notSortPlugins.push(v)
        } else {
          sortPlugins.push(v)
        }
      })
      sortPlugins.sort((a, b) => {
        return a.sortIndex - b.sortIndex
      })
      return [].concat(sortPlugins, notSortPlugins)
    },
    /**
     * 获取指定应用
    */
    getApp (appName) {
      return (allApps || []).find(v => v.name === appName)
    },
    /**
     * 获取所有应用
    */
    getAllApp () {
      return allApps || []
    },
    getRootApp () {
      return allApps.find(v => v.id === '__root__')
    },
    getRoute (routeId) {
      let result 
      this.getAllApp().some(app => {
        const rs = app.getRoute(routeId)
        if (rs) {
          result = rs
          return true
        }
      })
      return result
    },
    getClosestRoutes (routeId) {
      const result = []
      if (!routeId) {
        return result
      }
      const loop = (routes) => {
        if (!routes || !routes.length) {
          return
        }
        return routes.some(route => {
          if (!route.meta) {
            return
          }
          if (route.meta.$id === routeId) {
            result.push(route)
            return true
          }
          if (loop(route.children)) {
            result.push(route)
            return true
          }
        })
      }
      loop(appRoutes)
      return result
    },
    getAppModule (moduleId) {
      let result
      this.getAllApp().some(app => {
        const mod = app.getModule(moduleId)
        if (mod) {
          result = mod
          return true
        }
      })
      return result
    }
  }
  let nextStore = null
  Object.defineProperty(platform, 'store', {
    get () {
      return nextStore
    }
  })
  const updateRoute = (routeId, routeProps) => {
    loopRoutes(appRoutes, ({ route, update }) => {
      if (route.id === routeId) {
        update(Object.assign({}, route, routeProps))
        return true
      }
    })
  }
  const removeRoute = (routeId) => {
    loopRoutes(appRoutes, ({ route, remove }) => {
      if (route.id === routeId) {
        remove()
        return true
      }
    })
  }
  allApps.forEach(app => {
    if (!app) {
      return
    }
    app.setGetMenuMethod(({ app }) => {
      const routes = app.getRoutes()
      return routes.reduce((rs, route) => {
        // 如果是设置路由，那么就排除一下，因为设置路由目前计划是在企业设置里面使用，应用的时候不需要在菜单中展示
        if (route._settingRoute) {
          return rs
        }
        const menuItem = platform.getMenuItemByRouteId(route.id)
        // 如果是模块挂载的根路由，就取子chilren路由，目前这种是比较符合预期的，以后遇到其他场景在看
        if (route.moduleRoot && menuItem) {
          rs = rs.concat(menuItem.children || [])
        }
        return rs
      }, [])
    })
    app.fireCreated({
      platform
    })
    // 合并路由
    appRoutes = appRoutes.concat(app.getRoutes(options.basePath))
    app.fireLaunch({
      platform,
      updateRoute,
      removeRoute
    })
    // 合并store
    appStores.modules[app.name] = Object.assign(app.getStoreOptions() || {}, {
      namespaced: true // 应用内的状态强制增加命名空间
    })
    // 合并权限点
    Object.keys(app.getAccess()).forEach(k => {
      if (!Array.isArray(app.access[k])) {
        throw new Error('[App] 权限点必须是数组类型')
      }
      if (ACCESS[k]) {
        throw new Error(`[App] 不能重复定义${k}权限点`)
      }
      if (k.indexOf('Access_') !== 0) {
        throw new Error('[App] 权限点名称必须以Access_开头')
      }
      ACCESS[k] = app.access[k]
    })
    app._setReady()
  })
  platform.generateMenuData()
  if (options.onLaunch) {
    options.onLaunch({
      platform,
      routes: appRoutes,
      store: appStores,
      setStore (store) {
        nextStore = store
      },
      callback () {
        allApps.forEach(app => {
          if (!app) {
            return
          }
          app._setPlatform(platform)
          app.fireReady({
            platform
          })
        })
      }
    })
  }
  return platform
}

/**
 * 检查是否有权限
 * @param { Array } - 多个权限点集合
 * @example
 * checkRights(['sss', 'bbb']) // 需要sss和bbb都有权限,才返回true
*/
export function checkRights (access) {
  // if (!rootStore || !rootStore.getters.currentUserAccess) {
  //   return false
  // }
  // if (access && typeof access === 'string') {
  //   access = [access]
  // } else if (!access || !access.length) {
  //   return true
  // }
  // return !access.some(key => {
  //   const realPermission = ACCESS[key]
  //   if (!realPermission || !realPermission.length) {
  //     return true
  //   }
  //   return realPermission.some(v => {
  //     if (rootStore.getters.currentUserAccess[v]) {
  //       return false
  //     } else {
  //       return true
  //     }
  //   })
  // })
  return true
}

export function hideAppLoading () {
  const $pageLoading = document.getElementById('appLoading')
  if (!$pageLoading) {
    return
  }
  $pageLoading.style.display = 'none'
}

export function showAppLoading () {
  const $pageLoading = document.getElementById('appLoading')
  if (!$pageLoading) {
    return
  }
  $pageLoading.style.display = ''
}
